#if !defined(PERCEPTION_VISUALIZER_H_)
#define PERCEPTION_VISUALIZER_H_

#include <condition_variable>
#include <mutex>
#include <shared_mutex>
#include <thread>

#include "boost/json.hpp"
#include "common/circular_buffer.h"
#include "common/map_info.h"
#include "pcl/visualization/pcl_visualizer.h"

namespace autodrive {
namespace perception {

///@brief max dataframe buffer size
constexpr std::uint32_t kMaxViewerBufferSize{30U};

class PerceptionVisualizer final {
 public:
  PerceptionVisualizer(PerceptionVisualizer&&) = delete;
  PerceptionVisualizer(const PerceptionVisualizer&) = delete;
  PerceptionVisualizer& operator=(PerceptionVisualizer&&) = delete;
  PerceptionVisualizer& operator=(const PerceptionVisualizer&) = delete;
  ~PerceptionVisualizer();

  /// @brief init
  /// @param json_root conf json root node
  /// @return if no error return true, otherwise return false
  bool Init(boost::json::object& viewer_json_root) noexcept(false);

  ///@brief Get instance
  ///@return PerceptionVisualizer instance
  static PerceptionVisualizer& GetInstance() noexcept(false);

  /// @brief enqueue perception result
  /// @param timestamp timestamp
  /// @param src_data src map data
  /// @param filter_data filter map data
  /// @param truth_data truth map data
  void Enqueue(std::string timestamp, Map const* src_data,
               Map const* filter_data, Map const* truth_data) noexcept(false);

  bool IsFree() noexcept(false) {
    std::shared_lock<std::shared_mutex> lock(mutex_);
    return viewer_buffer_.IsEmpty() && !pcl_redraw_;
  }

 protected:
  PerceptionVisualizer();

  static void visualize(
      PerceptionVisualizer* const perceptionVisualizer) noexcept(false);

  static void pcl_visualize(
      PerceptionVisualizer* const perceptionVisualizer) noexcept(false);

  /// @brief set viewer style
  /// @param json_root conf json root node
  /// @return if no error return true, otherwise return false
  bool set_viewer_style(boost::json::object& viewer_json_root) noexcept(false);

  /// @brief dislplay lane dividers
  ///@param viewer visuaization ptr
  ///@param map map
  ///@param lane_divider_r lane_divider red
  ///@param lane_divider_g lane_divider green
  ///@param lane_divider_b lane_divider blue
  void display_lane_divider(
      pcl::visualization::PCLVisualizer::Ptr const& viewer, Map const& map,
      double lane_divider_r, double lane_divider_g, double lane_divider_b,
      int viewport_id, bool random_color) noexcept(false);

  /// @brief dislplay road edges
  ///@param viewer visuaization ptr
  ///@param map map
  ///@param road_edge_r road_edge red
  ///@param road_edge_g road_edge green
  ///@param road_edge_b road_edge blue
  void display_road_edge(pcl::visualization::PCLVisualizer::Ptr const& viewer,
                         Map const& map, double road_edge_r, double road_edge_g,
                         double road_edge_b, int viewport_id,
                         bool random_color) noexcept(false);

  /// @brief dislplay lane_divider_joints
  ///@param viewer visuaization ptr
  ///@param map map
  ///@param lane_divider_joints_r lane_divider_joints red
  ///@param lane_divider_joints_g lane_divider_joints green
  ///@param lane_divider_joints_b lane_divider_joints blue
  void display_lane_divider_joints(
      pcl::visualization::PCLVisualizer::Ptr const& viewer, Map const& map,
      double lane_divider_joints_r, double lane_divider_joints_g,
      double lane_divider_joints_b, int viewport_id,
      bool random_color) noexcept(false);

  /// @brief dislplay lane_divider_joints
  ///@param viewer visuaization ptr
  ///@param map map
  ///@param road_markings_r road_markings red
  ///@param road_markings_g road_markings green
  ///@param road_markings_b road_markings blue
  void display_road_markings(
      pcl::visualization::PCLVisualizer::Ptr const& viewer, Map const& map,
      double road_markings_r, double road_markings_g, double road_markings_b,
      int viewport_id, bool random_color) noexcept(false);

  /// @brief dislplay center_lines
  ///@param viewer visuaization ptr
  ///@param map map
  ///@param center_lines_r center_lines red
  ///@param center_lines_g center_lines green
  ///@param center_lines_b center_lines blue
  void display_center_lines(
      pcl::visualization::PCLVisualizer::Ptr const& viewer, Map const& map,
      double center_lines_r, double center_lines_g, double center_lines_b,
      int viewport_id, bool random_color) noexcept(false);

  /// @brief update zoom info
  void UpdataZoomInfo();

  /// @brief base display cloud
  ///@param viewer visuaization ptr
  ///@param pcl_cloud pcl cloud
  ///@param pt_size point size
  ///@param center_lines_r center_lines red
  ///@param center_lines_g center_lines green
  ///@param center_lines_b center_lines blue
  ///@param cloud_id cloud id
  ///@param viewport_id viewport id
  template <typename PointT>
  void base_display_cloud(
      pcl::visualization::PCLVisualizer::Ptr const& viewer,
      pcl::shared_ptr<pcl::PointCloud<PointT>> const pcl_cloud, double pt_size,
      double r, double g, double b, std::string const& cloud_id,
      int viewport_id) noexcept(false);

  /// @brief base display line
  ///@param viewer visuaization ptr
  ///@param pcl_cloud pcl cloud
  ///@param center_lines_r center_lines red
  ///@param center_lines_g center_lines green
  ///@param center_lines_b center_lines blue
  ///@param cloud_id cloud id
  ///@param viewport_id viewport id
  template <typename PointT>
  void base_display_line(
      pcl::visualization::PCLVisualizer::Ptr const& viewer,
      pcl::shared_ptr<pcl::PointCloud<PointT>> const pcl_cloud, double r,
      double g, double b, std::string const& line_id,
      int viewport_id) noexcept(false);

  /// @brief baee display polygon
  ///@param viewer visuaization ptr
  ///@param pcl_cloud pcl cloud
  ///@param center_lines_r center_lines red
  ///@param center_lines_g center_lines green
  ///@param center_lines_b center_lines blue
  ///@param cloud_id cloud id
  ///@param viewport_id viewport id
  template <typename PointT>
  void base_display_polygon(
      pcl::visualization::PCLVisualizer::Ptr const& viewer,
      pcl::shared_ptr<pcl::PointCloud<PointT>> const pcl_cloud, double r,
      double g, double b, std::string const& polygon_id,
      int viewport_id) noexcept(false);

 private:
  ///@brief init flag
  bool is_init_;

  ///@brief data frame buffer
  struct ViewerBufferStruct {
    std::string timestamp;
    Map const* map_src_;
    Map const* map_filter_;
    Map const* map_truth_;
  };
  CircularBuffer<ViewerBufferStruct, kMaxViewerBufferSize> viewer_buffer_;

  /// @brief
  ViewerBufferStruct current_data_;

  ///@brief is dispose
  bool is_dispose_;

  ///@brief visualize main task
  std::thread viewer_task_main_;

  /// @brief pcl view task
  std::thread pcl_viewer_task_;

  /// @brief pcl redraw flag
  bool pcl_redraw_;

  ///@brief mutex , multi thread safe
  mutable std::shared_mutex mutex_;
  /// @brief not redraw condition
  std::condition_variable_any not_redraw_condition_;

  /// @brief pcl point cloud
  pcl::PointCloud<pcl::PointXYZ>::Ptr point_cloud_ptr_;

  /// @brief pcl viewer plot id
  int pcl_viewer_src_{0};
  int pcl_viewer_filter_{0};
  int pcl_viewer_truth_{0};
  int pcl_viewer_info_{0};

  /// @brief zoom info
  float viewport_x_min_;
  float viewport_x_max_;
  float viewport_y_min_;
  float viewport_y_max_;
  pcl::PointXYZ viewport_zoom_center_;
  float viewport_zoom_distance_{0};
  float viewport_zoom_distance_scale_{1.5};

  /// @brief pcl viwre draw line
  bool is_draw_line_{false};

  /// @brief pcl viwre draw polygon
  bool is_draw_polygon_{false};

  /// @brief pcl viewer point size
  int viewer_point_size_{2};

  /// @brief pcl viewer keypoint size
  int viewer_keypoint_size_{10};

  /// @brief pcl viewer text size
  int viewer_text_size_{24};

  /// @brief pcl viewer car size
  int viewer_car_size_{8};

  struct ViwerDrawColor {
    double r;
    double g;
    double b;
  };
  ViwerDrawColor lane_divider_color_;
  ViwerDrawColor lane_divider_joints_color_;
  ViwerDrawColor road_edges_color_;
  ViwerDrawColor road_markings_color_;
  ViwerDrawColor center_lines_color_;
  ViwerDrawColor car_color_;

  bool filter_random_color_flag_{false};
};

}  // namespace perception
}  // namespace autodrive

#endif  // !PERCEPTION_VISUALIZER_H_
